//
// Created by mio on 25-4-11.
//

#include "EpollTaskScheduler.h"

#include <sys/epoll.h>
#include <errno.h>
#include <iostream>

EpollTaskScheduler::EpollTaskScheduler(int id) : TaskScheduler(id)
{
    // 创建 epoll 文件描述符。
    // 参数 1024 在现代内核中已无太大意义，但必须大于 0。
    epoll_fd_ = epoll_create(1024);
}

EpollTaskScheduler::~EpollTaskScheduler()
{

}

void EpollTaskScheduler::UpdateChannel(ChannelPtr channel)
{
    std::lock_guard<std::mutex> lock(mtx_);
    int fd = channel->GetSocket();
    if (channels_.find(fd) != channels_.end())
    {
        if (channel->IsNoneEvent()) // 如果 Channel 现在不关心任何事件了
        {
            Update(EPOLL_CTL_DEL, channel); // 从 epoll 中删除对此 fd 的监听
            channels_.erase(fd);
        }
        else // Channel 仍然关心事件，只是可能改变了关心的事件类型
        {
            Update(EPOLL_CTL_MOD, channel);
        }
    }
    else // Channel 是新的，之前未被监听
    {
        if (!channel->IsNoneEvent()) // 只有当 Channel 确实关心某些事件时，才将其添加到 epoll
        {
            channels_.emplace(fd,channel); // **重要：添加 Channel 到 channels_ map 中**
            Update(EPOLL_CTL_ADD, channel); // 将新的 fd 和事件添加到 epoll 监听
        }
    }
}

void EpollTaskScheduler::RemoveChannel(ChannelPtr channel)
{
    std::lock_guard<std::mutex> lock(mtx_);
    int fd = channel->GetSocket();
    if (channels_.find(fd) != channels_.end())
    {
        Update(EPOLL_CTL_DEL, channel);
        channels_.erase(fd);
    }
}

bool EpollTaskScheduler::HandleEvent(int timeout)
{
    epoll_event events[512] = { 0 };
    int num_events = -1;
    // 调用 epoll_wait 等待事件发生
    // 参数：epoll_fd, 事件数组, 最大事件数, 超时时间(毫秒)
    // 超时时间为 0 表示非阻塞：立即返回，无论是否有事件。
    // 这会导致 CPU 忙等待！应根据下一个定时器计算超时时间。
    // 例如: int timeout = GetNextTimeout(); // 需要添加获取下一个定时器超时的方法
    // num_events = epoll_wait(epoll_fd_, events, MAX_EPOLL_EVENTS, timeout);
    num_events = epoll_wait(epoll_fd_, events, 512, timeout);
    if (num_events < 0)
    {
        if (errno != EINTR)
        {
            // 其他错误，记录日志并可能返回 false 表示失败
            // TODO: 使用日志库记录错误 perror("epoll_wait");
            return false;
        }
    }
    for (int n = 0; n < num_events; n++)
    {
        if (events[n].data.ptr)
        {
            // 从 epoll_event 的 data 字段中获取关联的 Channel 指针
            ((Channel*)events[n].data.ptr)->HandleEvent(events[n].events);
        }
    }
    return true;
}

void EpollTaskScheduler::Update(int operation, ChannelPtr& channel)
{
    // // 准备 epoll_event 结构体
    // epoll_event event = { 0 };
    // // 对于 ADD 和 MOD 操作，需要设置事件类型和用户数据
    // if (operation != EPOLL_CTL_DEL)
    // {
    //     event.data.ptr = channel.get();
    //     event.events = channel->GetEvents();
    // }
    //
    // if (epoll_ctl(epoll_fd_, operation, channel->GetSocket(), &event) < 0)
    // {
    //     // TODO: 使用日志库记录详细错误信息
    //     std::cerr << "epoll_ctl failed, operation: " << operation
    //               << " fd: " << channel->GetSocket() << " errno: " << errno << std::endl;
    // }


    struct epoll_event event = { 0 };
    int fd = channel->GetSocket();
    event.data.ptr = channel.get();

    // 获取 Channel 关心的抽象事件类型
    int channel_events = channel->GetEvents();

    // --- 修改开始 ---
    // 将 Channel 抽象事件映射到具体的 epoll 事件
    if (operation != EPOLL_CTL_DEL) // ADD 或 MOD 操作需要设置事件
    {
        event.events = 0; // 初始化 epoll 事件标志
        if (channel_events & EVENT_IN) {
            // 如果关心读事件，则告诉 epoll 监听 IN, PRI, 和 RDHUP
            event.events |= EVENT_IN | EVENT_PRI | EVENT_RDHUP;
        }
        if (channel_events & EVENT_OUT) {
            // 如果关心写事件，则告诉 epoll 监听 OUT
            event.events |= EVENT_OUT;
        }
        // 注意：EPOLLERR 和 EPOLLHUP 不需要显式添加，epoll 会在发生时自动报告
    }
    // --- 修改结束 ---

    if (epoll_ctl(epoll_fd_, operation, fd, &event) < 0)
    {
        // TODO: 使用日志库记录详细错误信息
        std::cerr << "epoll_ctl failed, operation: " << operation
                  << " fd: " << fd << " errno: " << errno << std::endl;
    }
}
